/*
 * @(#)MultiplexInputStream.java	1.0 Mar 8, 2008
 *
 *	The MIT License
 *
 *	Copyright (c) 2008 Malachi de AElfweald <malachid@gmail.com>
 *
 *	Permission is hereby granted, free of charge, to any person obtaining a copy
 *	of this software and associated documentation files (the "Software"), to deal
 *	in the Software without restriction, including without limitation the rights
 *	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *	copies of the Software, and to permit persons to whom the Software is
 *	furnished to do so, subject to the following conditions:
 *
 *	The above copyright notice and this permission notice shall be included in
 *	all copies or substantial portions of the Software.
 *
 *	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *	THE SOFTWARE.
 */
package org.eoti.io.stream;

import static org.eoti.io.stream.MultiplexControl.*;
import java.io.FilterInputStream;
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.io.IOException;

public class MultiplexInputStream
		extends FilterInputStream
{
	protected boolean transmissionBlockEnded;

	public MultiplexInputStream(InputStream in)
	{
		super( new PushbackInputStream(in) );
		transmissionBlockEnded = false;
	}

	public int available()
	throws IOException
	{
		if(transmissionBlockEnded)
			return NONE.getCode(); // none left in this block

		int avail = super.available();

		if(EOS.matches(avail))
			return EOS.getCode(); // end of stream

		if(NONE.matches(avail))
			return NONE.getCode(); // none left in this block

		if(ETB.matches(avail))
		{
			transmissionBlockEnded = true;
			return NONE.getCode(); // none left in this block
		}

		byte[] buf = new byte[avail];
		int num = read(buf, 0, avail);
		((PushbackInputStream)in).unread( buf, 0, num);
		if(num > 0)
			transmissionBlockEnded = false; // read may have marked it true

		return num;
	}

	public int read()
	throws IOException
	{
        if(transmissionBlockEnded)
			return EOS.getCode(); // fake EOS

		int readByte = in.read();
		if(EOS.matches(readByte))
			return endTransmissionBlock();

		if(ETB.matches(readByte))
			return endTransmissionBlock();

		return readByte;
	}

	public int read(byte[] buf, int offset, int length)
	throws IOException
	{
		if(transmissionBlockEnded)
			return EOS.getCode(); // fake EOS

		for(int i=0; i<length; i++)
		{
			int readByte = in.read();
			if(EOS.matches(readByte))
			{
                endTransmissionBlock();
				return i - 1; // last non-EOS
			}else if(ETB.matches(readByte)){
                endTransmissionBlock();
				return i - 1; // last non-ETB
			}else if(!NULL.matches(readByte)){
                buf[offset + i] = (byte)readByte;
			}
		}

		return length; // if we got this far, we read entire length
	}

	public long skip(long length)
	throws IOException
	{
        if(transmissionBlockEnded)
			return (long)NONE.getCode();

		for(long pos=0; pos<length; pos++)
		{
			int readByte = in.read();
			if(EOS.matches(readByte))
			{
				endTransmissionBlock();
				return (long)(pos-1); // last non-EOS
			}else if(ETB.matches(readByte)){
				endTransmissionBlock();
				return (long)(pos-1); // last non-ETB
			}
		}

		return length; // if we got this far, we skipped entire length
	}

	public boolean hasTransmissionBlockEnded(){return transmissionBlockEnded;}

	public void resetTransmissionBlock(){transmissionBlockEnded=false;}

	public int endTransmissionBlock()
	{
		transmissionBlockEnded = true;
		return EOS.getCode();
	}

	public void close()
	{
		// ignore because there might be more blocks
	}

	public void finish()
			throws IOException
	{
		in.close();
	}
}
